# -*- coding:utf-8 -*-
import ctypes
import threading
import MySQLdb
import os
import socket
import json
import httplib
import urllib2
import uuid
import zipfile
import base64
import sys
import hashlib
import datetime
import subprocess
import math
from time import *

def commom_log(filename, content , write_type='wb'):
    try:
        f = open(filename , write_type)
        f.write(content);
        f.close()
    except:
        print('happen an exception when write file')
        return ""

def get_appid(url):
    index = url.rfind('id')
    index_right = url.find('?', index)
    if(index_right == -1):
        appid = url[index + 2: len(url)]
    else:
        appid = url[index + 2: index_right]
    return appid

def load_all_useraccount():
    global all_useraccount
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    global tb_name
    all_useraccount = list()
    f = open('itunes_account.txt' , 'r')
    try:
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        #关闭连接，释放资源   
        while True:
            record = f.readline()
            if(record == ''):
                break
            tmp_account = record.strip('\n')

            strlist    = tmp_account.split('|')
            strlist_1  = strlist[0]

            cur  = conn.cursor()
            sql  = 'select count(*) totalcount from '+tb_name+' where useraccount="%s" limit 1;'%(strlist_1)
            cur.execute(sql)
            result = cur.fetchone()
            if result[0] < 800000:
                all_useraccount.append(tmp_account+"|"+str(result[0]))
        cur.close()
        conn.close()
        return all_useraccount
    except MySQLdb.Error, e:
        print "Mysql Error:", e
        sys.exit(0)

def load_all_urls(start):
    global all_urls
    all_urls = list()
    f = open('itunesurl_'+str(start)+'.txt', 'r')
    while True:
        record = f.readline()
        if (record == ''):
            break
        all_urls.append(record.strip('\n'))
    return all_urls

def buy_app(appid, referUrl, account, password):
    global p_xigeserver_port
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        host = '127.0.0.1'
        port = p_xigeserver_port
        s.connect((host, port))
        print('connect xigeserver succeed\n')
        data = {'appid': appid, 'referUrl': referUrl, 'appleId': account,'password': password}
        str_data = json.dumps(data)
        s.send(str_data + '\r\n\r\n')
        buf = s.recv(4096*10)
        s.close()
        decodejson = json.loads(buf)
        return decodejson['result'], decodejson['szUrl'], decodejson['szKey'], decodejson['szSinf'], decodejson['szArtworkUrl'], decodejson['szMetadataPath'], decodejson['szVersion'], decodejson['szShortVersion'], decodejson['szBundleId']
    except Exception, e:
        return False, '', '', '', '', '', '', '', ''

def download_app(url, key, appid):
    global download_thread_num
    try:
        
        download_thread_num = download_thread_num + 1
        print str(download_thread_num) + ' download nums'
        
        cookie = 'downloadKey=%s'%key
        req = urllib2.Request(url)
        req.add_header('Cookie', cookie)
        req.add_header('User-Agent', 'Mozilla/4.0 (comatible; MSIE 8.0; Win32)')
        req.add_header('Connection', 'Keep-Alive')
        req.add_header('Cache-Control', 'no-cache')
        resp = urllib2.urlopen(req)
        content = resp.read()
        resp.close()
        if not os.path.exists('ipas'):
            os.mkdir('ipas')

        m = hashlib.md5(str(appid))
        m.digest()
        md5  = m.hexdigest()
        str1 = md5[0: 2]
        str2 = md5[2: 4]
        if not os.path.exists('ipas/'+str1):
            os.mkdir('ipas/'+str1)
        if not os.path.exists('ipas/'+str1+"/"+str2):
            os.mkdir('ipas/'+str1+"/"+str2)
        random_filename = 'ipas/'+ str1 + "/" + str2 + "/" + str(md5) + str2 + str1 + '.ipa'
        # print(random_filename)
        f = open(random_filename, 'wb')
        f.write(content)
        f.close()
        if(download_thread_num > 1):
            download_thread_num = download_thread_num -1
        return random_filename
    except:
        if(download_thread_num > 1):
            download_thread_num = download_thread_num -1
        print('happen an exception when download app url is %s key is %s'%(url, key))
        return ""

def download_png(url):
    try:
        req = urllib2.Request(url)
        resp = urllib2.urlopen(req)
        content = resp.read()
        resp.close()
        return True, content
    except:
        print('happen an exception when download png the url is %s'%url)
        return False, ""

def make_newipa(ipaname, png_content, sinf_content, appid, accounthash):
    try:
        zfobj = zipfile.ZipFile(ipaname)
        for name in zfobj.namelist():
            name = name.replace('\\', '/')
            if name.find('SC_Info/') > 0 and not name.endswith('SC_Info/'):
                basename = os.path.basename(name)
                break
        dotindex = basename.find('.')
        newsinfname = basename[0:dotindex] + '.sinf'
        newsinfpath = os.path.dirname(name) + '/' + newsinfname
        zfobj.close()

        # print('newsinfpath is %s'%newsinfpath)
        zf = zipfile.ZipFile(ipaname, "a")

        m = hashlib.md5(appid)
        m.digest()
        md5 = m.hexdigest()
        str1 = md5[0: 2]
        str2 = md5[2: 4]

        a_m = hashlib.md5(accounthash)
        a_m.digest()
        a_md5 = a_m.hexdigest()

        filename  = 'ipas/' + str1 +  '/' + str2 + '/' + str(uuid.uuid4())

        tempFile = open(filename, 'wb')
        base64_content = base64.decodestring(sinf_content)
        tempFile.write(base64_content)
        tempFile.close() 
        zf.write(filename, newsinfpath)

        tempFile = open(filename, 'wb')
        tempFile.write(png_content)
        tempFile.close() 
        zf.write(filename, 'iTunesArtwork')
        os.remove(filename)

        plist_name = 'c:/iTunes/'+str(appid)+'.plist'
        if not os.path.exists(plist_name):
            return False
        else:
            fplist = open(plist_name , 'r')
            meta_path = ''
            while 1:
                line = fplist.readline()
                if not line:
                    break
                else:
                    meta_path += line
            fplist.close()
            os.remove(plist_name)

            tempFile = open(filename, 'wb')
            meta_path = meta_path.split("</dict>")
            meta_path_new = ''
            count = 0
            for sub_data in meta_path:
                if len(meta_path) - 1 != count:
                    if len(meta_path) - 2 == count :
                        meta_path_new = meta_path_new + sub_data+"<key>account hash</key><string>"+a_md5+"</string></dict></plist>"
                    else:
                        meta_path_new = meta_path_new + sub_data+"</dict>"
                    count = count + 1
            if(meta_path_new == ''):
                meta_path_new = meta_path
            tempFile.write(meta_path_new)
            tempFile.close() 
            zf.write(filename, 'iTunesMetadata.plist')
            os.remove(filename)

            zf.close()
            return True
    except:
        print('when make_newipa happen an error')
        return False

def sec2date(secs):
    return strftime("%Y-%m-%d %H:%M:%S", localtime(secs))


def save_app_info(appid, state, appname, url, cookie, orign_url, bundleVersion, account, bundleId , version):
    global sqlite_mutex
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    global tb_name
    sqlite_mutex.acquire()
    try:
        timestamp = int(time())
        datetime  = sec2date(timestamp)
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        cur  = conn.cursor()
        # is_install 表示有 正版安装包
        sql  = 'update '+tb_name+' set status=%d, useraccount="%s",is_install=1,bundleid="%s",version="%s",bundleVersion="%s" where itunesid="%s"'%(state, account, bundleId,version,bundleVersion,appid)
        print(sql)
        cur.execute(sql)
        conn.commit()
        cur.close()
        conn.close()
    except MySQLdb.Error, e:
        print "Mysql Error:", e
    sqlite_mutex.release()


def del_buy_free_appp(appid):
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    global tb_name
    try:
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        cur  = conn.cursor()
        sql  = 'delete from '+tb_name+' where itunesid="%s" limit 1;'%(appid)
        print(sql)
        cur.execute(sql)
        conn.commit()
        #关闭连接，释放资源   
        cur.close()
        conn.close()
    except MySQLdb.Error, e:
        print "Mysql Error:", e

def load_all_urls_DB(start , limit):
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    global tb_name
    if os.path.isfile('itunesurl_'+str(start)+'.txt'):
        os.remove('itunesurl_'+str(start)+'.txt')

    try:
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        cur  = conn.cursor()
        # status 2 ： 新增 3 ： 更新
        # sql  = "select itunesid,itunes from "+tb_name+" where status=2 or status = 3;";
        sql  = "select itunesid,itunes from "+tb_name+" where status !=6 limit "+str(start)+","+str(limit)+";";
        cur.execute(sql)
        result = cur.fetchall()

        fp = open('itunesurl_'+str(start)+'.txt', 'wb')

        for f in result:
            fp.write(f[1] + "\n")

        conn.commit()
        #关闭连接，释放资源   
        cur.close()
        conn.close()
    except MySQLdb.Error, e:
        print "Mysql Error:", e

def check_app_status(appid):
    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    global tb_name
    try:
        conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
        conn.select_db(db_name)
        cur  = conn.cursor()
        sql  = "select status from "+tb_name+" where itunesid='%s' limit 1;"%(appid)
        cur.execute(sql)
        result = cur.fetchone()
        status = result[0]
        conn.commit()
        #关闭连接，释放资源   
        cur.close()
        conn.close()
        return status 
    except MySQLdb.Error, e:
        return -1

def check_plist_exists(random_filename):
    try:
        zf = zipfile.ZipFile(random_filename, "a")
        zf.getinfo("iTunesMetadata.plist")
        data = zf.read("iTunesMetadata.plist")
        if data :
            return 1
        else:
            return 2
    except:
        return 3

def thread_main(arg):
    global all_urls
    global all_account_cur
    global all_password_cur
    global all_account_cur_num  # 记录当前帐号 总的请求次数
    global p_xigeserver
    global return_error_nums

    while True:
        timestamp = int(time())
        datetime  = sec2date(timestamp)
        #加入线程锁
        threadname = threading.currentThread().getName()
        urls_mutex.acquire()
        if len(all_urls):
            url = all_urls.pop(0)
            appid = get_appid(url)
        else:
            urls_mutex.release()
            break
        if(all_account_cur==""):
            if len(all_useraccount):
                account_cur = all_useraccount.pop(0)
                strlist = account_cur.split('|')
                all_account_cur  = strlist[0]
                all_password_cur = strlist[1]
                all_account_cur_num = int(strlist[2])
            else:
                urls_mutex.release()
                break
        urls_mutex.release()
        print('current account : ' + all_account_cur +" ---  threadname: " + threadname +"\n")

        #检测 app 是否已经下载过了
        if check_app_status(appid) == 6:
            continue

        retvalue = buy_app(appid, url, all_account_cur, all_password_cur)
        timestamp = int(time())
        datetime  = sec2date(timestamp)
        # print retvalue
        print('buyapp ret 1'+ " : " + datetime + " == " + str(all_account_cur_num))
        #  `status` '0未购买 1购买成功 2ipa下载成功 3购买失败 4ipa下载失败 5 下载png失败 6 make_newipa ipa成功（这是才是最终的完成） 7make_newipa失败',
        # 返回值 1 成功 ，2004 帐号被禁用
        print retvalue[0]
        if retvalue[0] == '1':
            all_account_cur_num = all_account_cur_num + 1
            print('buy app succeed now download app')
            ipaname = download_app(retvalue[1], retvalue[2], appid)
            print('download app over')
            if ipaname == '':
                if retvalue[2] != '':# 已经购买过的ipas不能再次购买需要用其它的帐号购买
                    # save_app_info(appid, 4, '', retvalue[1], retvalue[2], url, retvalue[6], all_account_cur)
                    all_urls.append(url)
                    return_error_nums += 1
                    if return_error_nums > 5:
                        del_buy_free_appp(appid)    
                    continue
                else:
                    del_buy_free_appp(appid)
                    continue
            (result,png_content) = download_png(retvalue[4])
            if not result:
                # save_app_info(appid, 5, '', retvalue[1], retvalue[2], url, retvalue[6], all_account_cur)
                all_urls.append(url)
                continue
            if make_newipa(ipaname, png_content, retvalue[3], appid, all_account_cur):
                # retvalue[6] 表示 bundleVersion ，retvalue[6] 表示 shortBundleVersion ， retvalue[8] 表示 bundleid
                plist_value = check_plist_exists(ipaname)
                if plist_value == 1:
                    save_app_info(appid, 6, ipaname, retvalue[1], retvalue[2], url, retvalue[6], all_account_cur, retvalue[8] , retvalue[7])
                else:
                    commom_log('plist_error.log' , str(appid) + "|" + ipaname + "|" + str(plist_value) + "\n")
            else:
                # save_app_info(appid, 7, '', retvalue[1], retvalue[2], url, retvalue[6], all_account_cur)
                all_urls.append(url)
        elif retvalue[0]== '2004':
            all_urls.append(url) # 从新加回队列 继续下载 帐号被禁用
            print(all_account_cur+' is disabled'+" ---  threadname: " + threadname +"\n")
            continue
        elif retvalue[0] == '-2':
            all_urls.append(url) # 查询 server 失败
            continue
        elif  retvalue[0] == '-13':#限时免费 开始收费了 删除免费行列
            del_buy_free_appp(appid)
            continue
        elif retvalue[0] == '-14': #商品开始下架
            del_buy_free_appp(appid)
            continue
        elif retvalue[0] == False:
            all_urls.append(url) # 查询 server 失败
            return_error_nums += 1
            if return_error_nums > 5:
                p_xigeserver.kill()
                root_path = os.getcwd()
                root_path = root_path.replace("\\" , "/")

                p_xigeserver = subprocess.Popen(root_path+"/XiGeServer.exe "+str(p_xigeserver_port))
                return_error_nums = 0
            continue
        elif retvalue[0] == '-33':
            return_error_nums += 1
            if return_error_nums > 5:
                p_xigeserver.kill()
                root_path = os.getcwd()
                root_path = root_path.replace("\\" , "/")

                p_xigeserver = subprocess.Popen(root_path+"/XiGeServer.exe "+str(p_xigeserver_port))
                return_error_nums = 0

    print('thread exit')
    
def main():
    print('start')
    global sqlite_mutex
    global urls_mutex
    global all_account_cur
    global all_password_cur
    global all_account_cur_num
    global download_thread_num
    global return_error_nums

    global db_host
    global db_user
    global db_passwd
    global db_port
    global db_name
    global p_xigeserver
    global p_xigeserver_port
    global tb_name


    db_host  = "127.0.0.1"
    db_user  = 'root'
    db_passwd = ''
    db_port = 3306
    db_name = "app_xyapplist"
    tb_name = "t_xyapplist_biandh"

    if len(sys.argv) <=1 :
        print ('xigeserver port is null')
        sys.exit()
    else:
        p_xigeserver_port = int(sys.argv[1])

    download_total_nums = 0

    conn = MySQLdb.connect(host=db_host,user=db_user,passwd=db_passwd,port=db_port,init_command="set names utf8")
    conn.select_db(db_name)
    cur  = conn.cursor()
    sql  = "select count(*) from "+tb_name+" where status !=6 ;";
    cur.execute(sql)
    result = cur.fetchone()
    download_total_nums = result[0]
    conn.commit()
    cur.close()
    conn.close()

    db_limit = int(math.ceil(download_total_nums/5))

    db_start = 1
    if p_xigeserver_port == 1234:
        db_start = (db_start - 1) * db_limit
    elif p_xigeserver_port == 1235:
        db_start = 2
        db_start = (db_start - 1) * db_limit
    elif p_xigeserver_port == 1236:
        db_start = 3
        db_start = (db_start - 1) * db_limit
    elif p_xigeserver_port == 1237:
        db_start = 4
        db_start = (db_start - 1) * db_limit
    elif p_xigeserver_port == 1238:
        db_start = 5
        db_start = (db_start - 1) * db_limit
    else:
        print ('xigeserver not exists')
        sys.exit()

    download_thread_num = 0
    return_error_nums = 0
    all_account_cur = ''
    all_password_cur = ''
    all_account_cur_num  = 0

    load_all_urls_DB(db_start , db_limit)
    load_all_useraccount()
    socket.setdefaulttimeout(600)
    load_all_urls(db_start)
    

    root_path = os.getcwd()
    root_path = root_path.replace("\\" , "/")

    p_xigeserver = subprocess.Popen(root_path+"/XiGeServer.exe "+str(p_xigeserver_port))

    threads = []
    urls_mutex = threading.Lock()
    sqlite_mutex = threading.Lock()
    for x in xrange(0, 15):
        threads.append(threading.Thread(target=thread_main, args=(15,)))
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    print('end')
        
if __name__ == '__main__':
    reload(sys) 
    sys.setdefaultencoding('utf8')
    main()